package model;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.Random;
import java.util.concurrent.atomic.AtomicIntegerArray;

public class Cobra implements Runnable {

	// 0-3 para down,up,left,right
	private final int destino;
	final int cobra_id;
	public final Lock lock3 = new ReentrantLock(true);
	private AtomicIntegerArray validate;
	private volatile boolean has_turbo = false;
	private volatile int loose_cell;

	public int getCobra_id() {
		return cobra_id;
	}

	private World w;
	private volatile int tamanho = 6;
	private volatile boolean has_food = false;

	public synchronized int getTamanho() {
		return tamanho;
	}

	public synchronized void setTamanho(int tamanho) {
		this.tamanho = tamanho;
	}

	private volatile int clock = 500;

	private boolean has_jump = false;

	public boolean getJumpcount() {
		return has_jump;
	}

	public void setJumpcount(boolean jumpcount) {
		this.has_jump = jumpcount;
	}

	@Override
	public void run() {
		colocaCobra();
		// !!!
		try {
			mexe();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	private void mexe() throws InterruptedException {

		// autopilot!
		while (w.celulas_cobras.containsKey(cobra_id)
				&& !w.destinos_cobras.containsKey(cobra_id)
				&& w.jogo_a_decorrer) {

			movedir();

			// !!! modo manual de deslocação
			while ((w.celulas_cobras.containsKey(cobra_id)
					&& w.destinos_cobras.containsKey(cobra_id) && w.jogo_a_decorrer)) {

				moveManual();

			}
		}

	}

	private synchronized void moveManual() throws InterruptedException {

		populaBonus();
		final AtomicIntegerArray validate2 = new AtomicIntegerArray(1);
		boolean valido = false;
		int moveu = -1;
		boolean salto_usado = false;
		// System.out.println(w.destinos_cobras.get(cobra_id));

		while (moveu == -1) {

			// a alternar deslocacoes
			boolean pqsim = new Random().nextBoolean();

			if (w.destinos_cobras.get(cobra_id).get(1) > 0 && moveu == -1
					&& pqsim) {
				validate2.set(0, w.getDown(w.getCelulasCobra(cobra_id).get(0)));
				moveu = 0;

			}

			if (w.destinos_cobras.get(cobra_id).get(1) < 0 && moveu == -1
					&& pqsim) {
				validate2.set(0, w.getUp(w.getCelulasCobra(cobra_id).get(0)));
				moveu = 1;

			}
			if (w.destinos_cobras.get(cobra_id).get(0) > 0 && moveu == -1
					&& !pqsim) {
				validate2
						.set(0, w.getRight(w.getCelulasCobra(cobra_id).get(0)));
				moveu = 2;

			}
			if (w.destinos_cobras.get(cobra_id).get(0) < 0 && moveu == -1
					&& !pqsim) {
				validate2.set(0, w.getLeft(w.getCelulasCobra(cobra_id).get(0)));
				moveu = 3;
			}
			// System.out.println("cobra " + cobra_id + " com destino a "
			// + validate2);
		}
		// if para cima

		// System.out.println("cobra " + cobra_id + " com destino a " +
		// validate2);

		if (has_jump && validaDestino(has_jump, true, validate2) == 2) {
			switch (moveu) {
			case 0:
				validate2.set(0, w.getDown(validate2.get(0)));
				break;
			case 1:
				validate2.set(0, w.getUp(validate2.get(0)));
				break;

			case 2:
				validate2.set(0, w.getLeft(validate2.get(0)));
				break;

			case 3:
				validate2.set(0, w.getRight(validate2.get(0)));
				break;
			}
			valido = true;
			salto_usado = true;
		}
		if (has_food) {
			// System.out.println(validate2);
			if (validaDestino(has_jump, true, validate2) == 1
					|| validaDestino(has_jump, true, validate2) == 2) {
				// constroi o vector de valor, se definitivo e
				// valido
				++tamanho;
				validate = new AtomicIntegerArray(tamanho);

				for (int i = 1; i < tamanho; ++i) {
					validate.set(i, w.getCelulasCobra(cobra_id).get(i - 1));
				}
				validate.set(0, validate2.get(0));
				valido = true;

			} else {
				// System.out.println(valido);
				if (validaDestino(has_jump, true, validate2) == 1 || validaDestino(has_jump, true, validate2) == 2) {
					validate = w.getCelulasCobra(cobra_id);
					for (int i = tamanho - 1; i > 0; --i) {
						validate.set(i, validate.get(i - 1));
					}
					validate.set(0, validate2.get(0));
					valido = true;
				}
			}
		}
		if (valido && w.jogo_a_decorrer == true) {
			moveCobra(validate);
			aplicaBonus(validate);

			if (has_food) {
				w.decreaseFood_counter(cobra_id);
				has_food = false;
			}
			if (salto_usado) {
				w.setCobras_com_salto(cobra_id, false);
				has_jump = false;
			}
			eVencedora();

			switch (moveu) {
			case 0:
				w.destinos_cobras.get(cobra_id).decrementAndGet(1);
				if (w.destinos_cobras.get(cobra_id).get(0) == 0
						&& w.destinos_cobras.get(cobra_id).get(1) == 0) {
					w.destinos_cobras.remove(cobra_id);
					System.out.println("Cobra " + cobra_id
							+ "voltou a deslocacao aleatoria");
				}

				break;

			case 1:
				w.destinos_cobras.get(cobra_id).incrementAndGet(1);
				if (w.destinos_cobras.get(cobra_id).get(0) == 0
						&& w.destinos_cobras.get(cobra_id).get(1) == 0) {
					w.destinos_cobras.remove(cobra_id);
					System.out.println("Cobra " + cobra_id
							+ "voltou a deslocacao aleatoria");
				}
				break;

			case 2:
				w.destinos_cobras.get(cobra_id).decrementAndGet(0);
				if (w.destinos_cobras.get(cobra_id).get(0) == 0
						&& w.destinos_cobras.get(cobra_id).get(1) == 0) {
					w.destinos_cobras.remove(cobra_id);
					System.out.println("Cobra " + cobra_id
							+ "voltou a deslocacao aleatoria");
				}
				break;

			case 3:
				w.destinos_cobras.get(cobra_id).incrementAndGet(0);
				if (w.destinos_cobras.get(cobra_id).get(0) == 0
						&& w.destinos_cobras.get(cobra_id).get(1) == 0) {
					w.destinos_cobras.remove(cobra_id);
					System.out.println("Cobra " + cobra_id
							+ "voltou a deslocacao aleatoria");
				}
				break;
			}

		} else {
			w.destinos_cobras.remove(cobra_id);
			System.out.println(cobra_id + " com destino aleatorio.");
		}
	}

	public synchronized void moveCobra(AtomicIntegerArray new_loc) {

		for (int i = 1; i != new_loc.length(); ++i) {
			w.tabuleiro.get(i).desocupaCelula();

		}
		//w.celulas_cobras.remove(cobra_id);
		w.tabuleiro.get(new_loc.get(0)).reservaCelula(cobra_id);

		for (int i = 0; i != new_loc.length(); ++i) {
			w.tabuleiro.get(new_loc.get(i)).ocupaCelula(cobra_id, i == 0);
			loose_cell = new_loc.get(i);

		}
		w.celulas_cobras.put(cobra_id, new_loc);

		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		w.tabuleiro.get(loose_cell).desocupaCelula();

	}

	public synchronized void removeCobra(AtomicIntegerArray new_loc) {
		for (int i = 0; i != new_loc.length(); ++i) {
			w.tabuleiro.get(new_loc.get(i)).desocupaCelula();
		}
	}

	private synchronized void movedir() {

		populaBonus();
		final AtomicIntegerArray validate2 = new AtomicIntegerArray(1);
		boolean valido = false;
		boolean salto_usado = false;
		int dir = new Random().nextInt(4);
		switch (dir) {
		case 0:
			validate2.set(0, w.getDown(w.getCelulasCobra(cobra_id).get(0)));
			break;
		case 1:
			validate2.set(0, w.getUp(w.getCelulasCobra(cobra_id).get(0)));
			break;

		case 2:
			validate2.set(0, w.getLeft(w.getCelulasCobra(cobra_id).get(0)));
			break;

		case 3:
			validate2.set(0, w.getRight(w.getCelulasCobra(cobra_id).get(0)));
			break;
		}

		if (has_jump && validaDestino(has_jump, true, validate2) == 2) {
			switch (dir) {
			case 0:
				validate2.set(0, w.getDown(validate2.get(0)));
				break;
			case 1:
				validate2.set(0, w.getUp(validate2.get(0)));
				break;

			case 2:
				validate2.set(0, w.getLeft(validate2.get(0)));
				break;

			case 3:
				validate2.set(0, w.getRight(validate2.get(0)));
				break;
			}
			valido = true;
			salto_usado = true;
		}
		if (has_food) {
			// System.out.println(validate2);
			if (validaDestino(has_jump, true, validate2) == 1
					|| validaDestino(has_jump, true, validate2) == 2) {
				// constroi o vector de valor, se definitivo e
				// valido
				++tamanho;
				validate = new AtomicIntegerArray(tamanho);

				for (int i = 1; i < tamanho; ++i) {
					validate.set(i, w.getCelulasCobra(cobra_id).get(i - 1));
				}
				validate.set(0, validate2.get(0));
				valido = true;

			}
		} else {
			// System.out.println(valido);
			if (validaDestino(has_jump, true, validate2) == 1) {
				validate = w.getCelulasCobra(cobra_id);
				for (int i = tamanho - 1; i > 0; --i) {
					validate.set(i, validate.get(i - 1));
				}
				validate.set(0, validate2.get(0));
				valido = true;
			}
		}

		if (valido && w.jogo_a_decorrer) {
			moveCobra(validate);
			aplicaBonus(validate);

			if (has_food) {
				w.decreaseFood_counter(cobra_id);
				has_food = false;
			}
			if (salto_usado) {
				w.setCobras_com_salto(cobra_id, false);
				has_jump = false;
			}
			eVencedora();
			// System.out.println(cobra_id + "tenteu mexer-se para "
			// + validate);
		} else {
			// System.out.println(cobra_id + " à espera de novo destino.");
		}

	}

	private synchronized void populaBonus() {
		if (has_jump = w.get_salto_da_cobra(cobra_id))
			;
		if (w.getFood_counter(cobra_id) > 0) {
			has_food = true;
		}
		// if (has_turbo = w.getCobraTurbo(cobra_id) > 0)
		;
		// w.decCobraTurbo(cobra_id);

	}

	synchronized boolean haSalto(AtomicIntegerArray newcells) {

		boolean tem_salto = false;
		for (int a = 0; a != newcells.length(); ++a) {
			if (w.getCelula(newcells.get(a)).isJump())
				tem_salto = true;
		}
		return tem_salto;
	}

	private synchronized void colocaCobra() {

		boolean valido = false;
		resetValidate(destino);
		int counter = new Random().nextInt(w.getLado());

		switch (destino) {
		case 0:
			while (counter < w.getLado() & valido == false) {
				validate.set(0, w.getLado() * 5 + counter);
				validate.set(1, w.getLado() * 4 + counter);
				validate.set(2, w.getLado() * 3 + counter);
				validate.set(3, w.getLado() * 2 + counter);
				validate.set(4, w.getLado() + counter);
				validate.set(5, counter);
				++counter;

				if (validaDestino(has_jump, false, validate) != 3) {
					valido = true;
					break;
				}
			}
			break;

		case 1:
			while (counter < w.getLado() & valido == false) {
				validate.set(0, w.getLado() * w.getLado() - w.getLado() * 6
						+ counter);
				validate.set(1, w.getLado() * w.getLado() - w.getLado() * 5
						+ counter);
				validate.set(2, w.getLado() * w.getLado() - w.getLado() * 4
						+ counter);
				validate.set(3, w.getLado() * w.getLado() - w.getLado() * 3
						+ counter);
				validate.set(4, w.getLado() * w.getLado() - w.getLado() * 2
						+ counter);
				validate.set(5, w.getLado() * w.getLado() - w.getLado()
						+ counter);
				++counter;
				if (validaDestino(has_jump, false, validate) != 3) {
					valido = true;
					break;
				}
			}
			break;
		case 2:
			// for (int i = 0; i < w.getLado() & valido == false; ++i) {
			while (counter < w.getLado() & valido == false) {
				validate.set(0, w.getLado() - 6 + counter * w.getLado());
				validate.set(1, w.getLado() - 5 + counter * w.getLado());
				validate.set(2, w.getLado() - 4 + counter * w.getLado());
				validate.set(3, w.getLado() - 3 + counter * w.getLado());
				validate.set(4, w.getLado() - 2 + counter * w.getLado());
				validate.set(5, w.getLado() - 1 + counter * w.getLado());
				++counter;
				if (validaDestino(has_jump, false, validate) != 3) {
					valido = true;
					break;
				}
				;
			}
			break;
		case 3:
			while (counter < w.getLado() & valido == false) {
				validate.set(0, w.getLado() * counter + 5);
				validate.set(1, w.getLado() * counter + 4);
				validate.set(2, w.getLado() * counter + 3);
				validate.set(3, w.getLado() * counter + 2);
				validate.set(4, w.getLado() * counter + 1);
				validate.set(5, w.getLado() * counter);
				++counter;
				if (validaDestino(has_jump, false, validate) != 3) {
					valido = true;
					break;
				}
				;
			}
			break;
		}

		if (valido) {
			poeCobra(validate, cobra_id);
			removeCobra(validate);
			w.cobras_com_salto.put(cobra_id, false);
			w.setControlo_humano_cobras(cobra_id, false);
			w.food_counter.put(cobra_id, 0);
			w.turbo_cobras.put(cobra_id, 0);

		} else {
			System.out.println("Não Colocada cobra em " + validate);
		}

		counter = 0;
		valido = false;

	}

	public synchronized void poeCobra(AtomicIntegerArray new_loc, int cobra_id) {

		w.setCobras_com_salto(cobra_id, false);
		w.setControlo_humano_cobras(cobra_id, false);
		w.food_counter.put(cobra_id, 0);
		w.turbo_cobras.put(cobra_id, 0);

		// reset ao bonus

		for (int i = 0; i != new_loc.length(); ++i) {
			w.tabuleiro.get(new_loc.get(i)).reservaCelula(cobra_id);
			w.tabuleiro.get(new_loc.get(i)).ocupaCelula(cobra_id, i == 0);
		}
		w.celulas_cobras.put(cobra_id, new_loc);
		try {
			Thread.sleep(800);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	private synchronized void resetValidate(int dir) {
		lock3.lock();
		try {
			switch (dir) {
			case 0:
				validate.set(0, w.getLado() * 5);
				validate.set(1, w.getLado() * 4);
				validate.set(2, w.getLado() * 3);
				validate.set(3, w.getLado() * 2);
				validate.set(4, w.getLado());
				validate.set(5, 0);
				break;
			case 1:
				validate.set(0, w.getLado() * w.getLado() - w.getLado() * 5);
				validate.set(1, w.getLado() * w.getLado() - w.getLado() * 4);
				validate.set(2, w.getLado() * w.getLado() - w.getLado() * 3);
				validate.set(3, w.getLado() * w.getLado() - w.getLado() * 2);
				validate.set(4, w.getLado() * w.getLado() - w.getLado() * 1);
				validate.set(5, w.getLado() * w.getLado() - w.getLado());
				break;
			case 2:
				validate.set(0, w.getLado() - 5 * w.getLado());
				validate.set(1, w.getLado() - 4 * w.getLado());
				validate.set(2, w.getLado() - 3 * w.getLado());
				validate.set(3, w.getLado() - 2 * w.getLado());
				validate.set(4, w.getLado() - 1 * w.getLado());
				validate.set(5, w.getLado() - 0 * w.getLado());
				break;
			case 3:
				validate.set(0, w.getLado() + 5);
				validate.set(1, w.getLado() + 4);
				validate.set(2, w.getLado() + 3);
				validate.set(3, w.getLado() + 2);
				validate.set(4, w.getLado() + 1);
				validate.set(5, w.getLado());
				break;
			}
			;
		} finally {
			lock3.unlock();
		}
	}

	private synchronized int validaDestino(boolean tem_salto, boolean colisao,
			AtomicIntegerArray validate3) {

		final AtomicIntegerArray validate2 = validate3;
		// System.out.println(validate3);

		for (int iii = 0; iii <= w.max_cobras; ++iii) {
			if (w.celulas_cobras.containsKey(iii)) {
				// && !w.celulas_cobras.containsKey(cobra_id)) {
				// System.out.println(validate2);
				for (int ii = 0; ii != w.getCelulasCobra(iii).length(); ++ii) {
					for (int i = 0; i != validate2.length(); ++i) {

						if (w.getCelulasCobra(iii).get(ii) == validate2.get(i)) {

							if (colisao == false) {
								// criado para permitir colisoes geridas pela
								// reservacelula
								System.out.println(this.cobra_id
										+ "a repetir colocacao da " + iii);
								return 3;

							}
							if (iii == this.cobra_id) {
								// System.out.println("Cobra " + cobra_id+
								// " é suicida");
								return 0;
							}

							if (tem_salto) {
								// criado para permitir colisoes geridas pela
								// reservacelula
								System.out.println(this.cobra_id
										+ "vai saltar sobre a cobra" + iii);
								return 2;

							}
							if (colisao == true) {
								// criado para permitir colisoes geridas pela
								// reservacelula
								System.out.println(this.cobra_id
										+ "a colidir com " + iii
										+ ", deverá dormir!");

								return 1;

							}

						}
					}
				}

				for (int a = 0; a != validate2.length(); ++a) {
					if ((w.getCelula(validate2.get(a)).isBlock())
							&& w.jogo_a_decorrer) {
						// System.out.println(cobra_id
						// + " encontrou Celula com obstaculo");
						return 0;

					}
				}

			}

		}
		return 1;
	}

	protected synchronized boolean eVencedora() {
		boolean e_vencedora = false;

		switch (destino) {
		case 0:
			if ((w.celulas_cobras.get(cobra_id).get(0) + w.lado) / w.lado == w.lado) {
				e_vencedora = true;
				System.out
						.println("Cobra "
								+ cobra_id
								+ " venceu! A terminar a thread. Ultima Fila atingida.");
			}
			break;
		case 1:
			if (w.celulas_cobras.get(cobra_id).get(0) / w.lado == 0) {
				e_vencedora = true;
				System.out
						.println("Cobra "
								+ cobra_id
								+ " venceu! A terminar a thread. Primeira Fila atingida.");
			}
			break;
		case 3:
			if ((w.celulas_cobras.get(cobra_id).get(0) + 1) % w.lado == 0) {
				e_vencedora = true;
				System.out
						.println("Cobra "
								+ cobra_id
								+ " venceu! A terminar a thread. Fila da direita atingida.");
			}
			break;
		case 2:
			if ((w.celulas_cobras.get(cobra_id).get(0)) % w.lado == 0) {
				e_vencedora = true;
				System.out
						.println("Cobra "
								+ cobra_id
								+ " venceu! A terminar a thread. Fila da esquerda atingida.");
			}
			break;
		}
		if (e_vencedora) {
			w.jogo_a_decorrer = false;
			System.out.println("Cobra " + cobra_id
					+ " venceu! A terminar a thread. Destino " + destino
					+ " atingido.");
		}
		return e_vencedora;
	}

	void aplicaBonus(AtomicIntegerArray Celulas) {

		if (quantaComida(Celulas) > 0)
			w.increaseFood_counter(cobra_id);

		/*
		 * if (haTurbo(Celulas)) w.setCobraTurbo(cobra_id);
		 */

		if (haSalto(Celulas)) {
			w.setCobras_com_salto(cobra_id, true);
		}
		for (int i = 0; i != Celulas.length(); ++i) {
			w.tabuleiro.get(Celulas.get(i)).setNone();

		}

	}

	public synchronized int quantaComida(AtomicIntegerArray newcells) {

		int temcomida = 0;
		for (int a = 0; a != newcells.length(); ++a) {
			if (w.getCelula(newcells.get(a)).isFood())
				++temcomida;
		}
		return temcomida;
	}

	// construtor para threads de acordo com o enunciado
	public Cobra(World w, int i) {
		this.w = w;
		cobra_id = i;
		destino = new Random().nextInt(4);//
		validate = new AtomicIntegerArray(tamanho);
		// System.out.println(cobra_id);

	}
}
